﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Security.Cryptography;

namespace Sinacor.Infra.Common.Security.Cryptography
{
    public class RijndaelCryptographer
    {
        private static string Password = "N7Gg636hO1bXGPoozbYvprEZOzN9UjpVo8T43nw+2G7xT9YR2AeuD0pHT+we12OEPhUAP8N0aslWLLrium/bGw==";

        public static string EncryptString(string original)
        {
            PasswordDeriveBytes secretKey = null;
            byte[] encrypted = null;
            byte[] salt = null;

            salt = Encoding.ASCII.GetBytes(Password.Length.ToString());
            secretKey = new PasswordDeriveBytes(Password, salt);
            encrypted = EncryptString(original, secretKey.GetBytes(32), secretKey.GetBytes(16));

            return Convert.ToBase64String(encrypted);
        }

        public static byte[] EncryptString(string original, byte[] key, byte[] IVKey)
        {
            // Vericação de argumentos Válidos
            if (String.IsNullOrEmpty(original))
                throw new ArgumentNullException("original");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("key");
            if (IVKey == null || IVKey.Length <= 0)
                throw new ArgumentNullException("IV Key");

            // Declaração dos Streams utilizados para encriptar uma array de bytes em memória
            MemoryStream msEncrypt = null;
            CryptoStream csEncrypt = null;
            StreamWriter swEncrypt = null;

            // Declaração do objeto de encriptação.
            RijndaelManaged aesAlg = null;

            // Array de bytes onde o texto será encriptado
            byte[] encrypted = null;

            try
            {
                // Criação do objeto de encriptação.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key;
                aesAlg.IV = IVKey;

                // Cria o stream de encriptação
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Cria os stream utilizados para encriptação.
                msEncrypt = new MemoryStream();
                csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
                swEncrypt = new StreamWriter(csEncrypt);

                //Escreve no stream todo o texto que será encriptado
                swEncrypt.Write(original);
                swEncrypt.Flush();

                // Escreve os últimos 16 bytes que completam o stream de cryptografia.
                csEncrypt.FlushFinalBlock();

                encrypted = msEncrypt.ToArray();

            }
            finally
            {
                // Fecha todos os streams utilizados
                if (swEncrypt != null)
                {
                    swEncrypt.Close();
                    swEncrypt.Dispose();
                }
                if (csEncrypt != null)
                {
                    csEncrypt.Close();
                    csEncrypt.Dispose();
                }
                if (msEncrypt != null)
                {
                    msEncrypt.Close();
                    msEncrypt.Dispose();
                }

                // Limpa o objeto que contém o algoritimo de encriptação
                if (aesAlg != null)
                {
                    aesAlg.Clear();
                }
            }

            // retorna o array de bytes encriptado
            return encrypted;
        }

        public static string DecryptString(string cipherText)
        {
            PasswordDeriveBytes secretKey = null;
            string original = null;
            byte[] salt = null;

            salt = Encoding.ASCII.GetBytes(Password.Length.ToString());
            secretKey = new PasswordDeriveBytes(Password, salt);
            original = DecryptStringFromBytes(Convert.FromBase64String(cipherText), secretKey.GetBytes(32), secretKey.GetBytes(16));

            return original;
        }

        public static string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] IVKey)
        {
            // Vericação de argumentos Válidos
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IVKey == null || IVKey.Length <= 0)
                throw new ArgumentNullException("IV Key");

            // Declaração dos Streams utilizados para encriptar uma array de bytes em memória
            MemoryStream msDecrypt = null;
            CryptoStream csDecrypt = null;
            StreamReader srDecrypt = null;

            // Declaração do objeto de encriptação.
            RijndaelManaged aesAlg = null;

            // String onde o texto será desencriptado
            string original = null;

            try
            {
                // Criação do objeto de encriptação.
                aesAlg = new RijndaelManaged();
                aesAlg.Key = key;
                aesAlg.IV = IVKey;

                // Create a decrytor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

                // Cria os stream utilizados para encriptação.
                msDecrypt = new MemoryStream(cipherText);
                csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
                srDecrypt = new StreamReader(csDecrypt);

                //Lê o array de bytes encriptado desencriptando
                original = srDecrypt.ReadToEnd();
            }
            finally
            {
                // Fecha todos os streams utilizados
                if (srDecrypt != null)
                {
                    srDecrypt.Close();
                    srDecrypt.Dispose();
                }
                if (csDecrypt != null)
                {
                    csDecrypt.Close();
                    csDecrypt.Dispose();
                }

                if (msDecrypt != null)
                {
                    msDecrypt.Close();
                    msDecrypt.Dispose();
                }

                // Limpa o objeto que contém o algoritimo de encriptação
                if (aesAlg != null)
                    aesAlg.Clear();
            }

            // Retorna o texto desencriptado
            return original;
        }

        public class RijndaelManagedKeys
        {
            private RijndaelManaged _rj;

            public RijndaelManagedKeys()
            {
                _rj = new RijndaelManaged();
                _key = _rj.Key;
                _ivKey = _rj.IV;
            }

            private byte[] _key;
            public byte[] Key
            {
                get { return _key; }
            }
            private byte[] _ivKey;
            public byte[] IVKey
            {
                get { return _ivKey; }
            }
        }
    }
}
